package queue

import (
	"dmsv/agent"
	"dmsv/common"
	"dmsv/session"
	"fmt"
	"github.com/beego/beego/v2/core/logs"
	"strconv"
	"time"
)

type QueueEvent string

const (
	QUE_EVENT_TIME_OUT       QueueEvent = "timeout"
	QUE_EVENT_SUCCESS        QueueEvent = "success"
	QUE_EVENT_CALLER_HANGUP  QueueEvent = "hangup"
	QUE_EVENT_CALLEE_UNREACH QueueEvent = "callfail"
	QUE_EVENT_FIND_SERVICE   QueueEvent = "findService"
)

type DistribType string //ACD 方式

const (
	QUE_DISTRIB_NULL              DistribType = ""
	QUE_DISTRIB_BY_INDEX          DistribType = "byindex"     //轮询
	QUE_DISTRIB_BY_ANSWER_TIME    DistribType = "freeTime"    //最短接听时长  answerTime
	QUE_DISTRIB_BY_ANSWER_ACCOUNT DistribType = "answerCount" //最少接听次数
	QUE_DISTRIB_BY_LEVEL          DistribType = "byLevel"     //按优先级
	QUE_DISTRIB_BY_PROPORTION     DistribType = "proportion"  //按比例
	QUE_DISTRIB_BY_EQUEL          DistribType = "equel"       //平均

	QUE_WAIT_TIME = 120
	QUE_MAX_LEN   = 20
)

type Event struct {
	Name    QueueEvent
	Service *agent.AgentInfo
	Key     string
	QueName string
	QueId   string
	Ssion   *session.SessionInfo
}

func ServiceSignIn(appId, queId string, service *agent.AgentInfo) bool {

	id, err := strconv.Atoi(queId)
	if err != nil {
		logs.Error("get queue id failed:%v,appId:%v,queId:%v", err, appId, queId)
	}

	q, err := getQueHandle(appId, id)
	if err != nil {
		logs.Error("get queue info failed:%v,appId:%v,queId:%v", err, appId, queId)
		return false
	}

	q.SignIn(service)
	return true
}

func ServiceInDefault(appId string, service *agent.AgentInfo) {

	q, err := getQueHandleByName(appId, "default")
	if err != nil {
		logs.Error("get queue info failed:%v,appId:%v", err, appId)
		return
	}

	q.SignIn(service)
}

func ServiceSignOut(appId, queId string, service *agent.AgentInfo) bool {

	if queId == "" {
		qlst, err := getAllQueHandle(appId)
		if err != nil {
			return false
		}

		for _, q := range qlst {
			q.SignOut(service)
		}

	} else {

		id, err := strconv.Atoi(queId)
		if err != nil {
			logs.Error("get queue id failed:%v,appId:%v,queId:%v", err, appId, queId)
			return false
		}

		q, err := getQueHandle(appId, id)
		if err != nil {
			logs.Error("get queue info failed:%v,company:%v,name:%v", err, appId, queId)
			return false
		}
		q.SignOut(service)
	}

	return true
}

//创建队列,绑定到公司handle
func CreateNewQue(appId, name, key, media, companyAcd, way string, queId int, evChan chan *Event) *Queue {

	logs.Debug("create new 技能组:Id:%v,名称:%v for appId:[%v],acd:%v. ", queId, name, appId, way)

	q := &Queue{
		Id:         queId,
		appId:      appId,
		acd:        convertStrToDistrbType(way),
		companyAcd: convertStrToDistrbTypeDefault(companyAcd),
		key:        key,
		name:       name,
		media:      media,
		call:       make(chan enCallInfo, QUE_MAX_LEN),
		Agent:      make([]*agent.AgentInfo, 0, 0),
		waitTime:   QUE_WAIT_TIME,
		qLen:       QUE_MAX_LEN,
		eventChan:  evChan,
	}

	//创建任务队列
	go q.queueProcess(appId)

	addNewQue(appId, q)

	return q
}

//更新队列信息
func UpdateQue(appId, name, key, media, way string, id int) error {

	logs.Debug("update queue:%v->%v ,acd:%v ,media:%v", appId, name, way, media)

	gQueLock.Lock()
	defer gQueLock.Unlock()

	qlist, ok := gQueue[appId]
	if ok {
		for _, q := range qlist {
			if q.Id == id {
				if name != "" {
					q.name = name
				}

				if key != "" {
					q.key = key
				}

				if media != "" {
					q.media = media
				}

				if way != "" {
					q.acd = convertStrToDistrbType(way)
				}

				return nil
			}
		}
		return fmt.Errorf("not que named:%v,for company", name, appId)
	}
	return fmt.Errorf("not que for company :%v", appId)
}

//删除队列
func DelQue(appId, name string) {

	logs.Debug("del queue:%v->%v ", appId, name)

	gQueLock.Lock()
	defer gQueLock.Unlock()

	qlist, ok := gQueue[appId]
	if ok {
		for index, q := range qlist {
			if q.name == name {

				close(q.call) //关闭当前队列
				nlist := append(qlist[:index], qlist[index+1:]...)
				gQueue[appId] = nlist
				return
			}
		}
	}

	return
}

//删除队列
func DelQueById(appId, queId string) {

	logs.Debug("del queue:%v->%v ", appId, queId)

	gQueLock.Lock()
	defer gQueLock.Unlock()

	qlist, ok := gQueue[appId]
	if ok {
		for index, q := range qlist {
			if strconv.Itoa(q.Id) == queId {

				close(q.call) //关闭当前队列
				nlist := append(qlist[:index], qlist[index+1:]...)
				gQueue[appId] = nlist
				return
			}
		}
	}

	return
}

//更新企业级acd
func UpdateQueCompanyDistrib(appId, way string) {

	gQueLock.Lock()
	defer gQueLock.Unlock()

	qlist, ok := gQueue[appId]
	if ok {
		for _, q := range qlist {
			q.companyAcd = convertStrToDistrbTypeDefault(way)
		}
	}
	return
}

func AddNewCallToQueue(appId, queName string, ss *session.SessionInfo) bool {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	if qs, ok := gQueue[appId]; ok {
		for _, q := range qs {
			//进入指定队列
			if len(queName) > 0 && q.name == queName {

				past := time.Since(ss.GetEnQueTime())
				logs.Debug("%v time out judge,入队时长:%v", ss.GetSessionId(), past)

				if !ss.GetEnQueTime().IsZero() && past > time.Duration(q.waitTime)*time.Second {

					return false
				}

				return q.callEnQueue(ss, "")
			}
		}
	}
	logs.Error("%v cannot find same queue:%v", ss.GetSessionId(), queName)
	return false
}

func GetQueLen(appId string) int {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	if qs, ok := gQueue[appId]; ok {
		return len(qs)
	}

	return 0
}

func AddNewCallToQueueByKey(appId, key, lastService string, ss *session.SessionInfo) bool {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	if qs, ok := gQueue[appId]; ok {
		for _, q := range qs {
			//进入指定队列
			if q.key == key {
				return q.callEnQueue(ss, lastService)
			}
		}
	}
	logs.Debug("[%v]未找到技能组队列。appid [%v],key [%v]入队失败.", ss.GetSessionId(), appId, key)
	return false
}

func AddNewCallToQueueById(appId, lastService string, queId int, call *session.SessionInfo) bool {

	gQueLock.RLock()
	defer gQueLock.RUnlock()

	if qs, ok := gQueue[appId]; ok {
		for _, q := range qs {
			if q.Id == queId {
				if len(q.call) >= q.qLen {
					call.HangUp(common.ErrQueueFull)
					return false
				}

				logs.Debug("[%v] 入队成功,技能组Id[%v],技能组名称 [%v] 接听策略:[%v] 队列长度:[%v],坐席数量:[%v].",
					call.GetSessionId(), q.Id, q.name, q.distrib(), len(q.call), len(q.Agent))
				call.UpdateEnQueTime(queId, q.name)

				//呼叫中心业务,发送入队成功通知
				ev := &Event{
					Name:    QUE_EVENT_SUCCESS,
					Ssion:   call,
					QueName: q.name,
					QueId:   fmt.Sprintf("%d", queId),
				}
				q.eventChan <- ev

				nCall := enCallInfo{
					call:        call,
					lastService: lastService,
				}
				q.call <- nCall

				return true
			}
		}
		logs.Error("%v Appid:%v 未找到对应的技能组:%v", call.GetSessionId(), appId, queId)
	} else {
		logs.Error("%v 无对应Appid:%v", call.GetSessionId(), appId)
	}

	call.HangUp(common.ErrSystem)
	return false
}

func Init() {
	gQueue = make(map[string][]*Queue)
}
